<?php
// $Id: Dispatcher.php 27 2010-11-16 12:49:54Z mjohnson60@gmail.com $


/**
 * Enter description here ...
 * 
 * @author Mark Phillip Johnson
 * @version 1.0
 * @copyright Copyright (c) 2010, markjohnson
 * @license http://www.gnu.org/licenses/gpl.html The GNU General Public License v3.0
 *
 */
class Dispatcher extends Object
{
	/**
	 * @var Dispatcher Enter description here ...
	 */
	private static $instance = NULL;
	
	/**
	 * @var array Enter description here ...
	 */
	private static $notifications = array();
	
	/**
	 * @var array Enter description here ...
	 */
	private static $observers = array();

	/**
	 * Enter description here ...
	 * 
	 */
	public function __construct()
	{
		self::$instance = & $this;
		
		Loader::import('Notification');
		
		self::$notifications['#allnotifications'] = array();
	}

	/**
	 * @return Dispatcher
	 */
	public static function &getInstance()
	{
		if (!isset(self::$instance))
		{
			new Dispatcher();
		}
		
		return self::$instance;
	}

	/**
	 * Enter description here ...
	 * 
	 * @param Object $observer
	 * @param string $callback
	 * @param string $notification
	 * @param Object $sender
	 */
	public static function addObserver(Object $observer, string $callback, string $notification = NULL, Object $sender = NULL)
	{
		$obsHash = spl_object_hash($observer);
		if (!isset(self::$observers[$obsHash]))
		{
			self::$observers[$obsHash] = array();
		}
		
		if (!empty($notification))
		{
			if (!isset(self::$notifications[$notification]))
			{
				self::$notifications[$notification] = array();
				self::$notifications[$notification]['#allsenders'] = array();
			}
			
			if (!isset(self::$observers[$obsHash][$notification]))
			{
				self::$observers[$obsHash][$notification] = array();
			}
			
			if (!empty($sender))
			{
				$hash = spl_object_hash($sender);
				if (!isset(self::$notifications[$notification][$hash]))
				{
					self::$notifications[$notification][$hash] = array();
				}
				
				if (!isset(self::$observers[$obsHash][$notification][$hash]))
				{
					self::$observers[$obsHash][$notification][$obsHash] = TRUE;
				}
				
				self::$notifications[$notification][$hash][$obsHash] = array(
					$observer, 
					$callback
				);
			}
			else
			{
				if (!isset(self::$observers[$obsHash][$notification]['#allsenders']))
				{
					self::$observers[$obsHash][$notification]['#allsenders'] = TRUE;
				}
				
				self::$notifications[$notification]['#allsenders'][$obsHash] = array(
					$observer, 
					$callback
				);
			}
		}
		else
		{
			self::$observers[$obsHash]['#allnotifications'] = TRUE;
			self::$notifications['#allnotifications'][$obsHash] = array(
				$observer, 
				$callback
			);
		}
	}

	/**
	 * Enter description here ...
	 * 
	 * @param Notification $notification
	 */
	public static function postNotification(Notification $notification)
	{
		foreach (self::$notifications['#allnotifications'] as $obsHash => $value)
		{
			call_user_func_array($value, array(
				$notification
			));
		}
		
		if (!empty(self::$notifications[$notification->name]))
		{
			$observers = self::$notifications[$notification->name];
			
			if (isset($observers['#allsenders']))
			{
				foreach ($observers['#allsenders'] as $obsHash => $value)
				{
					call_user_func_array($value, array(
						$notification
					));
				}
			}
			
			$hash = spl_object_hash($notification->object);
			if (isset($observers[$hash]))
			{
				foreach ($observers[$hash] as $obsHash => $value)
				{
					call_user_func_array($value, array(
						$notification
					));
				}
			}
		}
	}

	/**
	 * Enter description here ...
	 * 
	 * @param string $name
	 * @param Object $object
	 */
	public static function postNotificationWithNameAndObject(string $name, Object $object)
	{
		$notification = Notification::notificationWithNameAndObject($name, $object);
		Dispatcher::postNotification($notification);
	}

	/**
	 * Enter description here ...
	 * 
	 * @param string $name
	 * @param Object $object
	 * @param array $userInfo
	 */
	public static function postNotificationWithNameObjectAndUserInfo(string $name, Object $object, array $userInfo)
	{
		$notification = Notification::notificationWithNameObjectAndUserInfo($name, $object, $userInfo);
		Dispatcher::postNotification($notification);
	}

	/**
	 * Enter description here ...
	 * 
	 * @param Object $observer
	 */
	public static function removeObserver(Object $observer)
	{
		$obsHash = spl_object_hash($observer);
		if (isset(self::$observers[$obsHash]))
		{
			foreach (self::$observers[$obsHash] as $notification => $sender)
			{
				unset(self::$notifications[$notification][$sender][$obsHash]);
			}
			
			unset(self::$observers[$obsHash]);
		}
	}

	/**
	 * Enter description here ...
	 * 
	 * @param Object $observer
	 * @param string $name
	 * @param Object $object
	 */
	public static function removeObserverWithNameAndObject(Object $observer, string $name, Object $object)
	{
		$obsHash = spl_object_hash($observer);
		$senderHash = spl_object_hash($object);
		
		unset(self::$notifications[$name][$senderHash][$obsHash]);
		unset(self::$observers[$obsHash][$name][$senderHash]);
	}
}
